package f.h.dragon.patterns.money;

/**
 * Money: A simple Money.
 * 
 * @author Wenlong Meng(wenlong.meng@gmail.com)
 * @version 1.0 at 2012/04/21
 * @since 1.0
 */
public class Money implements IMoney {

	//local variables
	/**
	 * amount
	 */
	private int fAmount;
	/**
	 * currency
	 */
	private String fCurrency;

	//Constructor
	/**
	 * Constructs a money from the given amount and currency.
	 * 
	 * @param amount
	 * @param currency
	 */
	public Money(int amount, String currency) {
		this.fAmount = amount;
		this.fCurrency = currency;
	}
	
	//getter
	/**
	 * the mount of this money.
	 * 
	 * @return
	 */
	public int amount() {
		return fAmount;
	}

	/**
	 * the currency of this money.
	 * 
	 * @return
	 */
	public String currency() {
		return fCurrency;
	}

	//Logic
	/**
	 * Adds a money to this money. Forwards the request to the addMoney helper.
	 * 
	 * @param m
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#add(f.h.dragon.patterns.money.IMoney)
	 */
	@Override
	public IMoney add(IMoney m) {
		return m.addMoney(this);
	}

	/**
	 * Adds a simple Money to this money. This is a helper method for
	 * implementing double dispatch
	 * 
	 * @param m
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#addMoney(f.h.dragon.patterns.money.Money)
	 */
	@Override
	public IMoney addMoney(Money m) {
		if (m.currency().equals(currency()))
			return new Money(amount() + m.amount(), currency());
		return MoneyBag.create(this, m);
	}

	/**
	 * Adds a MoneyBag to this money. This is a helper method for implementing
	 * double dispatch
	 * 
	 * @param s
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#addMoneyBag(f.h.dragon.patterns.money.MoneyBag)
	 */
	@Override
	public IMoney addMoneyBag(MoneyBag s) {
		return s.addMoney(this);
	}

	/**
	 * Tests whether this money is zero
	 * 
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#isZero()
	 */
	@Override
	public boolean isZero() {
		return amount() == 0;
	}

	/**
	 * Multiplies a money by the given factor.
	 * 
	 * @param factor
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#multiply(int)
	 */
	@Override
	public IMoney multiply(int factor) {
		return new Money(amount() * factor, currency());
	}

	/**
	 * Multiplies a money by the given factor.
	 * 
	 * @param factor
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#negate()
	 */
	@Override
	public IMoney negate() {
		return new Money(-amount(), currency());
	}

	/**
	 * Subtracts a money from this money.
	 * 
	 * @param m
	 * @return
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#subtract(f.h.dragon.patterns.money.IMoney)
	 */
	@Override
	public IMoney subtract(IMoney m) {
		return add(m.negate());
	}

	/**
	 * Append this to a MoneyBag m.
	 * 
	 * @param m
	 * 
	 * @see f.h.dragon.patterns.money.IMoney#appendTo(f.h.dragon.patterns.money.MoneyBag)
	 */
	public/* this makes no sense */void appendTo(MoneyBag m) {
		m.appendMoney(this);
	}
	
	//equals
	/**
	 * Tests whether these moneys are equal.
	 * 
	 * @param anObject
	 * @return
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object anObject) {
		if (isZero())
			if (anObject instanceof IMoney)
				return ((IMoney) anObject).isZero();
		if (anObject instanceof Money) {
			Money aMoney = (Money) anObject;
			return aMoney.currency().equals(currency()) && amount() == aMoney.amount();
		}
		return false;
	}

	//hashCode
	/**
	 * Returns a hash code value for the object.
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return fCurrency.hashCode() + fAmount;
	}


	//toString
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("[" + amount() + " " + currency() + "]");
		return buffer.toString();
	}

}
